# --------------------------------------------------------------------------------------------------------------------
# Project:             Digital Payment System (DPS)
# Website:             http://code.google.com/p/dps-x509/
# Purpose of document: This class extracts information out of the Asn1 encoded
#                      csr file
# --------------------------------------------------------------------------------------------------------------------
import sys, re

sys.path.append("..")

try:
  import simplejson as json
except ImportError:
  import json

#TODO: remove
#from pyasn1.codec.der import decoder, encoder

#we would prefer the try to pass, but the import stuff isn't working
try:
  import keyczar.keys as keys
  import keyczar.util as util
except ImportError:
  from keyczar import *

import dps.errors
import dps.utils
import dps.x509AttributeAndValue
import dps.csrParams
import dps.x509Extension
import dps.x509Helper

# --------------------------------------------------------------------------------------------------------------------
class CsrAsn1Extractor:
  """ Responsible for transforming ASN.1 data into python objects 
  
  important conversions to know:
  
  We are dealing with 3 main datatypes:
    - Asn1:   U{http://en.wikipedia.org/wiki/Asn1}
    - Bytes:  U{http://en.wikipedia.org/wiki/Bytes}
    - Base64: U{http://en.wikipedia.org/wiki/Base64}
  """  
  
# --------------------------------------------------------------------------------------------------------------------
  def __init__(self, cert):
    """ open der or base64 certificate string
    @param   cert: must be in der or base64 string formats. cert must be a single item and not a chain
    @type    cert: string
    @raise   dps.errors.DecodingError: if unable to create asn1 model from cert
    @return: CsrAsn1Extractor
    """
    try:
      base64certs = dps.x509Helper.certsToBase64(cert)
      dps.utils.assertion(len(base64certs) == 1, \
                          "csrAsn1Extractor.__init__: extractor only takes one certifcate at a time")
      
      certBytes = dps.x509Helper.base64ToBytes(base64certs[0])
      
      self.__asn1Cert_ = dps.x509Helper.bytesToAsn1(certBytes)
      
      dps.utils.out("certificate: "+self.__asn1Cert_.prettyPrint())
      
      #This should work but is a limitation of CSR under pyasn1!
      #dps.utils.assertion(dps.x509Helper.asn1ToBytes(self.__asn1Cert_) == certBytes, "Reencoding does not match")

    except dps.errors.DPSBaseError:
      raise     
    except Exception, e:
      dps.utils.out("Exception in csrAsn1Extractor.__init__: " + str(sys.exc_info()[0]) + str(e))
      raise dps.errors.DecodingError()
  
  # --------------------------------------------------------------------------------------------------------------------
  def getSubjectBase64(self):
    """ 
    returns base64 string of subject details 
    @rtype: string
    """
    ret = ""
    try:
      subjectAsn1 = self.__asn1Cert_.getComponentByName("tbsCertificate").getComponentByName("subject")
      ret = dps.x509Helper.bytesToBase64(dps.x509Helper.asn1ToBytes(subjectAsn1))
    except dps.errors.DPSBaseError:
      raise
    except Exception, e:
      dps.utils.out("Exception in csrAsn1Extractor.getSubjectBase64: " + str(sys.exc_info()[0]) + str(e))
      raise dps.errors.MissingRequiredParametersError("Unable to retrieve issuer information")   
    
    return ret      
    
    # --------------------------------------------------------------------------------------------------------------------
  def getSubject(self):
    """ 
    returns the subject
    @raise dps.errors.MissingRequiredParametersError:
    @rtype: x509AttributeAndValueList 
    """
    subjectList = None
    try:
      subjectAsn1 = self.__asn1Cert_[0][1]
      subjectAsn1 = dps.x509Helper.bytesToAsn1(dps.x509Helper.asn1ToBytes(subjectAsn1), \
                                               dps.asn1CertificateSpecification.Name())
      subjectList = dps.x509AttributeAndValue.X509AttributeAndValueList.loadFromAsn1(subjectAsn1)
      
    except dps.errors.DPSBaseError:
      raise
    except Exception, e:
      dps.utils.out("Exception in csrAsn1Extractor.getSubject: " + str(sys.exc_info()[0]) + str(e))
      raise dps.errors.MissingRequiredParametersError("Unable to retrieve subject information")   
    
    return subjectList

  # --------------------------------------------------------------------------------------------------------------------
  def getRsaPublicKey(self):
    """ 
    returns key 
    @rtype: keyczar.keys.RsaPublicKey 
    """
    pubRsaKey = None
    try:
      pubAsn1 = self.__asn1Cert_[0][2]
      pubBase64 = dps.x509Helper.bytesToBase64(dps.x509Helper.asn1ToBytes(pubAsn1))
      
      pubKeyParams = util.ParseX509(pubBase64)
      modBytes = util.TrimBytes(util.BigIntToBytes(pubKeyParams["n"]))
      pubKeyBytes = util.TrimBytes(util.IntToBytes(pubKeyParams["e"]))
      modulusBase64 = dps.x509Helper.bytesToBase64(modBytes)
      pubKeyBase64 = dps.x509Helper.bytesToBase64(pubKeyBytes)
      nbits = len(modBytes) * 8
      
      rsaParams = json.dumps({"publicExponent": pubKeyBase64, "modulus": modulusBase64, "size": nbits})
      dps.utils.out("csrAsn1Extractor.getRsaPublicKey: " + rsaParams)
      pubRsaKey = keys.RsaPublicKey.Read(rsaParams)
      
      dps.utils.assertion(isinstance(pubRsaKey, keys.RsaPublicKey), \
                          "csrAsn1Extractor.getRsaPublicKey: invalid pubRsaKey type")
    except dps.errors.DPSBaseError:
      raise
    except Exception, e:
      dps.utils.out("Exception in csrAsn1Extractor.getRsaPublicKey: " + str(sys.exc_info()[0]) + str(e))
      raise dps.errors.NoRSADataFoundError()
      
    return pubRsaKey
  
  # --------------------------------------------------------------------------------------------------------------------
  def getTbsCertificateBytes(self):
    """ 
    returns only the tbsCertificate portion of the file in bytes
    @rtype: string
    """
    certBytes = None
    try:
      certBytes = dps.x509Helper.asn1ToBytes(self.__asn1Cert_[0])
      dps.utils.assertion(isinstance(certBytes, str), "csrAsn1Extractor.getTbsCertificateBytes: invalid cert type")

    except dps.errors.DPSBaseError:
      raise
    except Exception, e:
      dps.utils.out("Exception in csrAsn1Extractor.getTbsCertificateBytes: " + str(sys.exc_info()[0]) + str(e))
      raise dps.errors.DecodingError()
    
    return certBytes
    
  # --------------------------------------------------------------------------------------------------------------------
  def getSignatureBytes(self):
    """ 
    returns on the signature portion of the file in bytes 
    @rtype: string
    """
    sigBytes = None
    try:
      sigAsn1 = self.__asn1Cert_[2]
      sigBinary = ""
      for bit in sigAsn1:
        sigBinary += str(bit)
      sigBytes = util.BinToBytes(sigBinary)
      dps.utils.assertion(isinstance(sigBytes, str), "csrAsn1Extractor.getSignatureBytes: invalid sigBytes type")
    except dps.errors.DPSBaseError:
      raise
    except Exception, e:
      dps.utils.out("Exception in csrAsn1Extractor.getSignatureBytes: " + str(sys.exc_info()[0]) + str(e))
      raise dps.errors.DecodingError()
    
    return sigBytes   
    
  # --------------------------------------------------------------------------------------------------------------------
  def asBase64(self):
    """ 
    base64 representation without header and footer
    @rtype: string
    """
    return dps.x509Helper.bytesToBase64(dps.x509Helper.asn1ToBytes(self.__asn1Cert_))
    
  # --------------------------------------------------------------------------------------------------------------------
  def getParams(self):
    """ 
    extract dps.csrParams from certificate
    @rtype: dps.csrParams.CsrParams
    """
    params = dps.csrParams.CsrParams()
    
    params.setSubject(self.getSubject())
    params.setPublicKey(self.getRsaPublicKey())
    
    return params
  
  # --------------------------------------------------------------------------------------------------------------------
  def prettyPrint(self):
    """ 
    returns string from asn1 pretty print 
    @rtype: string 
    """
    return self.__asn1Cert_.prettyPrint()

# --------------------------------------------------------------------------------------------------------------------
if __name__ == "__main__":  
  try:
    
    
    import dps.x509Asn1Extractor
    
    certdata = """-----BEGIN CERTIFICATE-----
MIICoDCCAgmgAwIBAgIFAJH+SqIwDQYJKoZIhvcNAQEFBQAwUDELMAkGA1UEBhMC
QVUxDDAKBgNVBAgTA0FDVDERMA8GA1UEBxMIQ2FuYmVycmExIDAeBgNVBAMMF3Rp
bW9fbWF0aGFzQGhvdG1haWwuY29tMB4XDTEwMDkxMTAyMDc1OVoXDTEzMDkxMTAy
MDc1OVowUDELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA0FDVDERMA8GA1UEBxMIQ2Fu
YmVycmExIDAeBgNVBAMMF3RpbW9fbWF0aGFzQGhvdG1haWwuY29tMIGfMA0GCSqG
SIb3DQEBAQUAA4GNADCBiQKBgQC99Jed+KPnROHY5NPJZidyZ8N9ItHM/w4kd0hB
Lobbk2vlvpv4Cy2TCeRXZH6VHGudnsqpuxNvjaimNC/aHKZc1EL5Ej/BZcRoWVom
aaJB6f1UFwZZcq8FoUop8Kr03RYLoZqBJagqW47kgF3RHTgykTaYOZmdoRUY45go
IqVyKwIDAQABo4GFMIGCMBQGCWCGSAGG+EIBAQEB/wQEAwIEsDAgBgNVHQ4BAf8E
FgQUunY/thpP15uZmXhCSv3qVlthxUwwKgYDVR0lAQH/BCAwHgYIKwYBBQUHAwIG
CCsGAQUFBwMDBggrBgEFBQcDBDAOBgNVHQ8BAf8EBAMCA7gwDAYDVR0TAQH/BAIw
ADANBgkqhkiG9w0BAQUFAAOBgQAjBGZs3HcTn8PrODsf4mE4nwdh56zZYHuvQfHz
2aPk607esIiFQjwUpRuwqz1ToY+yTmDCAl17+9xgv1Ky+Qb+gZDt2KXCcEInE4oI
KQCADyjmbbVXFl2RK2YzVOfYGBFSMQzKcdwt+KnwiilqGimnBo5p7L96m4nb5rwD
7Z9GdQ==
-----END CERTIFICATE-----"""
    
    cert = dps.x509Asn1Extractor.X509Asn1Extractor(certdata)
    
    
    #print certsToBase64(der)
    
    #base64 = open("../certificates/testbase64.crt").read()
    base64 = """MIIBnTCCAQYCAQAwXTELMAkGA1UEBhMCU0cxETAPBgNVBAoTCE0yQ3J5cHRvMRIw
EAYDVQQDEwlsb2NhbGhvc3QxJzAlBgkqhkiG9w0BCQEWGGFkbWluQHNlcnZlci5l
eGFtcGxlLmRvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAr1nYY1Qrll1r
uB/FqlCRrr5nvupdIN+3wF7q915tvEQoc74bnu6b8IbbGRMhzdzmvQ4SzFfVEAuM
MuTHeybPq5th7YDrTNizKKxOBnqE2KYuX9X22A1Kh49soJJFg6kPb9MUgiZBiMlv
tb7K3CHfgw5WagWnLl8Lb+ccvKZZl+8CAwEAAaAAMA0GCSqGSIb3DQEBBAUAA4GB
AHpoRp5YS55CZpy+wdigQEwjL/wSluvo+WjtpvP0YoBMJu4VMKeZi405R7o8oEwi
PdlrrliKNknFmHKIaCKTLRcU59ScA6ADEIWUzqmUzP5Cs6jrSRo3NKfg1bd09D1K
9rsQkRc9Urv9mRBIsredGnYECNeRaK5R1yzpOowninXC"""
    base64 = """MIICUzCCATsCAQAwDjEMMAoGA1UEAxMDdGltMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAndyh3NZzIdKko7snKaIJrAAtVUS+vpQjEJW0lJA/zKsI7OxF
Ye0EsCi9JpNfLK2Vqaataqjui9QJdZLmHi3cTo7uQKdsL851V8QsJt+FOeKvws3q
BQe0wj/Tu1+QD2F0Oh4ArWjhxaCg7YAEKDLDLHgM599Npzq+QpF3EYyJqN2kK9Il
/+T6pd7/0DjJMbzuE6qx9CEIPTwPtaQ51+4G9nfTR9iNg8OdSVLbHyMAbynxvAY/
U1ZJSiYYdlwFp7H/Uc/SqI7j1e7HY63/OAoyZ/smHZeNrCzBRPH1GUagg7O9QE3C
oEKGM9QZM4LGBl3hjgkDbgpSxcXnO9EnbbnQewIDAQABAAAwDQYJKoZIhvcNAQEE
BQADggEBAF9YmwuN4hwpRRELR0oIw2R93kfxhHdOBcOOY/ld0YfJk9UE7knYcjVB
msQipL2W+xotmLBFJUCBP7pYyIHYQ0VfHDB/chRzvkKv8fkzA7SJFtICvRSqC773
QDGkG6l6/sQMFF43kOlTMTbsZdPxOJ/j3BeuJddfeANdaNIW1gZwK4DiJOF8e2F/
xGzsDRBTwi5ne/RCOgBT3uFG1T17EH9JHorpi2pOo4Fa9CsKFJbcrTB5xIX16uq4
8MNbiKS9lY5FPmzrJFbVqPt9yp/ZGzWWbmzeyNSlVvWO5eUzMAIqaa3QjAcK4bu5
DSrnvbe9TXgreVmK81qNq7ftK4itirI==="""
    #base64 = open("../certificates/testbase64.crt").read()
    
    c = CsrAsn1Extractor(base64)
    
    print c.getParams()
    #c.getSignatureBytes()
    #c.isSelfSigned()
    #c.getTbsCertificateBytes()
    #certBase64 = c.getBase64()
    #util.WriteFile(certBase64, "my_2.crt")
    
    #c.prettyPrint()
    
  except Exception, e:
    dps.utils.out("An exception occured: " + str(sys.exc_info()[0]) + " " + str(e))

